#include <video-graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

// Alex a pé
#include "xpm/alexL.xpm"
#include "xpm/alexR.xpm"
#include "xpm/alexWL1.xpm"
#include "xpm/alexWL2.xpm"
#include "xpm/alexWL3.xpm"
#include "xpm/alexWR1.xpm"
#include "xpm/alexWR2.xpm"
#include "xpm/alexWR3.xpm"
#include "xpm/alexJL.xpm"
#include "xpm/alexJR.xpm"
// Águia
#include "xpm/eagle1L.xpm"
#include "xpm/eagle1R.xpm"
#include "xpm/eagle2L.xpm"
#include "xpm/eagle2R.xpm"

#include "xpm/wall.xpm"
#include "xpm/cloud.xpm"
#include "xpm/cake.xpm"
#include "xpm/heliL1.xpm"
#include "xpm/heliL2.xpm"
#include "xpm/heliR1.xpm"
#include "xpm/heliR2.xpm"
#include "xpm/moneyB.xpm"
#include "xpm/moneyS.xpm"

#include "xpm/boss1A.xpm"
#include "xpm/boss1B.xpm"
#include "xpm/boss2A.xpm"
#include "xpm/boss2B.xpm"
#include "xpm/boss3A.xpm"
#include "xpm/boss3B.xpm"
#include "xpm/relva.xpm"
#include "xpm/morcegoA.xpm"
#include "xpm/morcegoB.xpm"
#include "xpm/frog.xpm"
#include "xpm/frogJump.xpm"
#include "xpm/EscorpiaoR.xpm"
#include "xpm/EscorpiaoL.xpm" 
#include "xpm/cabecaA.xpm"
#include "xpm/cabecaB.xpm"
#include "xpm/bolaL.xpm"
#include "xpm/bolaR.xpm"

#include "xpm/galo_rock.xpm"
#include "xpm/galo_paper.xpm"
#include "xpm/galo_scissor.xpm"
#include "xpm.h"
#include "level.h"
#include "bigxpm.h"

/** Resolução vertical dos XPMs brutos */
#define XPM_DEF_VRES 480

/** Resolução horizontal dos XPMs brutos */
#define XPM_DEF_HRES 640

/* Altura de uma célula do nível */
int xpm_level_cell_height;

/* Largura de uma célula do nível */
int xpm_level_cell_width; 

/** Fator de escala */
float xpm_scale;

/** Lê ficheiros XPM gravados usando a paleta de cores que está em toos/DOS.gpl
 * \param map Variável definida no ficehiro XPM
 * \param wd Endereço onde a largura do XPM é armazenada
 * \param ht Endereço onde a altura do XPM é armazenada
 * \return O array de pixels lidos do XPM alocado com malloc
 */
static unsigned char *read_xpm_gimp (char *map[], unsigned int *wd, unsigned int *ht)
{
	int width, height, colors, nbyte;
	unsigned char sym[256 * 256];
	int i, j;
	unsigned char *pix;
	char *line;
	
	// read width, height, colors
	if (sscanf(map[0],"%d %d %d %d", &width, &height, &colors, &nbyte) != 4) {
		printf("read_xpm: incorrect width, height, colors, nbyte\n");
		return NULL;
	}

#ifdef DEBUG
  	printf("%d %d %d %d\n", width, height, colors, nbyte);
#endif
	*wd = width;
	*ht = height;
	
	if (nbyte != 2) {
		printf("read_xpm2: incorrect number of bytes\n");
		return NULL;
	}
	
	for (i=0; i<256*256 ; i++)
		sym[i] = 0;
	// read symbols <-> colors 
	for (i=0; i<colors; i++) {
		sym[map[i+1][0] * 256 + map[i+1][1]] = i;
	}
	
	// allocate pixmap memory
	pix = malloc(width*height);

	// parse each pixmap symbol line
	for (i=0; i<height; i++) {
		line = map[colors+1+i];
#ifdef DEBUG
		printf("\nparsing %s\n", line);
#endif
		for (j = 0; j < width; j++) {
			pix[i * width +j] = sym[ line[2*j] * 256 + line[2*j + 1]];
		}
	}
	return pix;
}

/** Array de XPMs carregados */
static XPM loaded_xpms[XPM_NUMBER];

/** Array que mapeia identificadores->variáveis dos ficheiros XPM
 * \warning Este array precisa estar sincronizado com enum XPMID
 */
static char ** xpm_vars[XPM_NUMBER] = {
	// Alex a pé
	alexL_xpm,
	alexR_xpm,
	alexWL1_xpm,
	alexWL2_xpm,
	alexWL3_xpm,
	alexWR1_xpm,
	alexWR2_xpm,
	alexWR3_xpm,
	alexJL_xpm,
	alexJR_xpm,
	// Águia
	eagle1L_xpm,
	eagle1R_xpm,
	eagle2L_xpm,
	eagle2R_xpm,
	morcegoA_xpm,
	morcegoB_xpm,
	wall_xpm, // Parede
	cloud_xpm, // Nuvem
	cake_xpm, // Trocar para o bolo
	relva_xpm,
    Frog_xpm,
    frogJump_xpm,
    EscorpiaoR_xpm,
    EscorpiaoL_xpm, 
    cabecaA_xpm,
    cabecaB_xpm,
    bolaL_xpm,
    bolaR_xpm,
	
	// Helicóptero
	heliL1_xpm,
	heliL2_xpm,
	heliR1_xpm,
	heliR2_xpm,

	// Dinheiros
	moneyS_xpm,
	moneyB_xpm,
	// Bosses
	boss1A_xpm,
	boss1B_xpm,
	boss2A_xpm,
	boss2B_xpm,
	boss3A_xpm,
	boss3B_xpm,
	// Jogo do galo
	galo_rock_xpm,
	galo_paper_xpm,
	galo_scissor_xpm,

	NULL,	// Menu
	NULL	// Credits
};

/* Inicialização */
void xpm_init (void)
{
	int i;

	// Nenhum XPM carregado
	for (i = 0; i < XPM_NUMBER; i++) {
		loaded_xpms[i] = NULL;
	}
	xpm_scale = ((double)HRES)/(double)XPM_DEF_HRES;
	xpm_level_cell_width = xpm_scale * LEVEL_CELL_WIDTH;
	xpm_level_cell_height = xpm_scale * LEVEL_CELL_HEIGHT;
}

/* Libera a memória usada pelos XPMs */
void xpm_finish (void)
{
	int i;

	for (i = 0; i < XPM_NUMBER; i++) {
		if (loaded_xpms[i] != NULL) {
			free (loaded_xpms[i]->pixels);
			free (loaded_xpms[i]);
		}
	}

}

/* Filtro da mediana 2x2 * /
static char med_filt2 (XPM xpm, unsigned int x, unsigned int y)
{
	// Garantimos que x e y são válidos
	if (x >= xpm->width-1) 
		x = xpm->width - 2;
	if (y >= xpm->height-1) 
		y = xpm->height - 2;

	// Construímos um array com os 4 pixels
	char array [4]; // 4 = 2*2
	array [0] = xpm_ref (xpm, x, y);
	array [1] = xpm_ref (xpm, x, y+1);
	array [2] = xpm_ref (xpm, x+1, y);
	array [3] = xpm_ref (xpm, x+1, y+1);
	// Merge sort (mais ou menos)

#define MF_EXCHANGE(m,n) {char temp; temp = array[m]; array[m]=array[n]; array[n] = temp;}
	//Ordena cada metade do array
	if (array[0] > array[1]) MF_EXCHANGE(0,1);
	if (array[2] > array[3]) MF_EXCHANGE(2,3);
	// Põe o valor correto nas estremidades
	if (array[0] > array[2]) MF_EXCHANGE(0,2);
	if (array[1] > array[3]) MF_EXCHANGE(1,3);
	// Ordena o meio
	if (array[1] > array[2]) MF_EXCHANGE(1,2);
	// O array está ordenado
#undef MF_EXCHANGE

	// Obtém a mediana
	if (array[1] == array[0]) {
		return array[1];
 	} else {
		return array[2];
	}
} */

XPM xpm_get (enum XPMID id)
{
	// Se o XPM já existe, o carregamos
	if (loaded_xpms [id] != NULL) {
		return loaded_xpms[id];
	}
	// Não foi carregado ainda, então o carregamos

	// Lê o XPM bruto
	unsigned int w, h;
	unsigned char* spr;
	// Devido a bug no DJGPP
	if (xpm_vars[id] == NULL) {
		spr = read_xpm_gimp(bigxpm_get(id), &w, &h);
	} else {
		spr = read_xpm_gimp(xpm_vars[id], &w, &h);
	}


	// O escalamos
	XPM xpm = malloc (sizeof (struct XPM_));
	xpm->height = h * xpm_scale;
	xpm->width = w * xpm_scale;


	xpm->pixels = malloc (xpm->height * xpm->width);

	// Resize
	int x, y;
	for (y = 0; y < xpm->height; y++) {
		for (x = 0; x < xpm->width; x++) {
			float xsrc = x / xpm_scale; // Posição X na imagem original
			float ysrc = y / xpm_scale; // Posição Y na imagem original

			int xsrc_i = floor (xsrc);
			int ysrc_i = floor (ysrc);
			
			// Pesos dos pixels
			float xweight = xsrc - xsrc_i;
			float yweight = ysrc - ysrc_i;

			if (xweight > 0.5) xsrc_i++;
			if (yweight > 0.5) ysrc_i++;
			xpm->pixels[y * xpm->width + x] = spr[ysrc_i * w + xsrc_i];
		}
	}

	// Guardamos o XPM criado
	loaded_xpms[id] = xpm;
	// Destruímos os pixels do xpm bruto
	free (spr);
	// retornamos o xpm
	return xpm;
}

